package com.example.ch01wwwssocom.controller;

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.ch01wwwssocom.pojo.SysUser;
import com.example.ch01wwwssocom.service.UserService;
import com.example.ch01wwwssocom.utils.AESTool;
import com.example.ch01wwwssocom.utils.JwtUtil;
import com.example.ch01wwwssocom.vo.ResultJson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/***
 * @title AuthController
 * @description <description class purpose>
 * @author WeiShuo
 * @version 1.0.0
 * @create 2023/9/20 17:50
 **/
@Slf4j
@Controller
public class AuthController {

    @Autowired
    private UserService userService;

    @GetMapping("/login")
    String toLogin(String returnUrl, HttpServletRequest request) {

        /**
         *
         * 核心代码：利用客户端服务 重定向到 sso服务，浏览器以sso网页地址，发起login请求，从而间接取到sso服务的cookie
         * (如果浏览器客户端服务有登录过，就会产生 cookie)
         *
         */
        String valueByCookie = getJwtValueByCookie(request);
        if (valueByCookie == null) {
            return "login";
        }

        //过期的或者不符合规范的，让其重新登录
        boolean ok = checkToken(valueByCookie);
        if(!ok){
            return "login";
        }


        return "redirect:" + returnUrl + "?token=" + valueByCookie;

    }

    private String getJwtValueByCookie(HttpServletRequest servletRequest) {

        Cookie[] cookies = servletRequest.getCookies();
//        servletRequest1.get
        if (cookies == null) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("tokenId")) {
                return cookie.getValue();
            }

        }
        return null;
    }

    /**
     * 统一验证登录中心
     *
     * @param username 用户名
     * @param password 密码
     */
    @PostMapping("/doLogin")
    public void checkLogin(String username, String password, String returnUrl, HttpServletResponse response) {
        SysUser user = userService.login(username, password);
        String jwtValue = null;
        if (user != null) {
            /*
             * 获得uuid,作为tokenId（TGC）
             * 将tokenId存进jedis，返回客户端以jwt存储的tokenId，
             * 即使tokenId被截取也无所谓
             */
            String tokenId = UUID.randomUUID().toString();

            jwtValue = createToken(tokenId, user, null);
            if (jwtValue != null) {
//                Jedis jedis = jedisPool.getResource();
//                jedis.set(tokenId, user.toString());
//                jedis.expire(tokenId,1800);
//                log.info("查看key的剩余生存时间："+jedis.ttl(tokenId));
            }

            //将jwt加密的TGC存进cookie
            Cookie cookie = new Cookie("tokenId", jwtValue);
            cookie.setPath("/"); //设置根域名
            cookie.setHttpOnly(true);
            response.addCookie(cookie);
            try {
                response.sendRedirect(returnUrl + "?token=" + jwtValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }

        System.out.println("-----------------------------:登录失败！！！");
    }

    /**
     * 刷新token
     *
     * @param refreshToken 刷新token
     *
     */
    @ResponseBody
    @PostMapping("/refreshToken")
    public ResultJson refreshToken(@RequestBody HashMap<String, Object> params) {

        String refreshToken = params.get("refreshToken")+"";
        String newToken = getNewToken(refreshToken);
        if (newToken == null){
           return ResultJson.failure();
        }
        return ResultJson.success(newToken, "成功");
    }


    /**
     * 得到新的token
     * @param refreshToken 用于刷新的token
     */
    private String getNewToken(String refreshToken) {
        if(StringUtils.isBlank(refreshToken)){
            return null;
        }

        DecodedJWT decodedJWT = verifyRefreshToken(refreshToken);
        if (decodedJWT == null) {
            return null;
        }

        //String tokenId = decodedJWT.getSubject();
        String tokenId = decodedJWT.getClaim("tokenId").asString();
        String username = decodedJWT.getClaim("username").asString();
        return createToken(tokenId, new SysUser(username, null), refreshToken);

    }


    /**
     * 检查token 以及 refreshtoken 是否过期
     * 如果refreshToken 没有过期，可以将token给客户系统，让它去利用refreshToken 服务使用
     *
     * @param token
     * @return 返回结果，让其是否需要重新登录
     */
    private boolean checkToken(String token) {


        DecodedJWT decodedJWT = null;
        try {
            decodedJWT = JwtUtil.verifyToken(JwtUtil.hs256(null), token);

        } catch (TokenExpiredException e) {
            log.error("token过期异常", e);

            DecodedJWT decodedJWT1 = JwtUtil.getDecodedJWT(token);
            String encodeRefreshToken = decodedJWT1.getClaim("refreshToken").asString();

            /**
             * 解码
             */
            String secret = "77A7A1D6-STST-40CC-U787-49EC3576XXXX";
            AESTool aesTool = new AESTool(secret);

            String refreshToken = null;
            try {
                refreshToken = aesTool.decrypt(encodeRefreshToken);
            } catch (Exception ex) {
                throw new RuntimeException(ex);


            }
            if(StringUtils.isNotBlank(refreshToken)){
                DecodedJWT decodedJWT2 = verifyRefreshToken(refreshToken);
                if (decodedJWT2 != null){
                    return true;
                }
            }
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token过期异常");
        } catch (
                AlgorithmMismatchException e) {
            log.error("算法不正确异常", e);

//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                InvalidClaimException e) {
            log.error("无效的claim异常", e);

//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                SignatureVerificationException e) {
            log.error("签名验证的异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                JWTDecodeException e) {
            log.error("编码不正确的异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (JWTVerificationException e) {
            log.error("token无效异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        }

        return decodedJWT != null;


    }


    /**
     * 创建token
     *
     * @param tokenId
     * @param user
     * @param refreshToken 刷新token , 可为空
     * @return
     */
    private String createToken(String tokenId, SysUser user, String refreshToken) {

        Algorithm algorithm = JwtUtil.hs256(null);
        Date nowTime = Calendar.getInstance().getTime();
        //失效日期
        Calendar calendar = Calendar.getInstance();
//        calendar.add(Calendar.MINUTE, 10);
        calendar.add(Calendar.SECOND, 5);
        Date expTime = calendar.getTime();


        HashMap<String, String> claimMap = new HashMap<>();
        claimMap.put("username", user.getUserName());


        /**
         * 检查是否有刷新token
         */
        //创建刷新token: 5天
        String secret = "77A7A1D6-STST-40CC-U787-49EC3576XXXX";
        AESTool aesTool = new AESTool(secret);
        try {
            if (refreshToken == null) {
                String encrypted = aesTool.encrypt(createRefreshToken(tokenId, claimMap));
                claimMap.put("refreshToken", encrypted);
            }else {
                String encrypted = aesTool.encrypt(refreshToken);
                claimMap.put("refreshToken", encrypted);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return JwtUtil.createToken("client", algorithm, nowTime, expTime, tokenId, UUID.randomUUID().toString(), claimMap);
    }


    /**
     * 刷新token密钥
     */
    private final String refreshTokenSecret = "66adc82f-7fbf-408e-b8e9-09c3d0443f65xxx";

    /**
     * 创建刷新token
     *
     * @return
     */
    private String createRefreshToken(String tokenId, HashMap<String, String> claimMap) {
        Algorithm algorithm = JwtUtil.hs256(refreshTokenSecret);
        Date nowTime = Calendar.getInstance().getTime();
        //失效日期
        Calendar calendar = Calendar.getInstance();
//        calendar.add(Calendar.DATE, 5);
        calendar.add(Calendar.MINUTE, 1);
        Date expTime = calendar.getTime();

        claimMap.put("tokenId", tokenId);
        return JwtUtil.createToken("client", algorithm, nowTime, expTime, "refreshToken", UUID.randomUUID().toString(), claimMap);
    }

    /**
     * 认证刷新 token
     *
     * @param token
     * @return 通过返回结果, 不然就返回null
     */
    private DecodedJWT verifyRefreshToken(String token) {

        DecodedJWT decodedJWT = null;
        try {
            decodedJWT = JwtUtil.verifyToken(JwtUtil.hs256(refreshTokenSecret), token);

        } catch (TokenExpiredException e) {
            log.error("token过期异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token过期异常");
        } catch (
                AlgorithmMismatchException e) {
            log.error("算法不正确异常", e);

//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                InvalidClaimException e) {
            log.error("无效的claim异常", e);

//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                SignatureVerificationException e) {
            log.error("签名验证的异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (
                JWTDecodeException e) {
            log.error("编码不正确的异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        } catch (JWTVerificationException e) {
            log.error("token无效异常", e);
//            throw new GlobalException(ResultCode.WXMA_INVALID_TOKEN_CODE, "token无效异常");
        }

        return decodedJWT;


    }

}
